[org 0x1000]

dw 0x55aa; 魔数，用于判断错误

mov si,loading
call print

;开始内存检测
detect_memory:
    xor ebx,ebx
    ;es:di 结构体的缓存位置
    mov ax,0
    mov es,ax
    mov edi,ards_buffer
    mov edx,0x534d4150  ;固定签名

    .next:
        ;子功能号
        mov eax,0xe820
        ;ards 结构的大小(字节)
        mov ecx,20
        ;系统调用
        int 0x15
        ;如果CF置位，则表示出错
        jc error
        ;将缓存指针指向下一个结构体
        add di,cx
        ;将结构体数量加一
        inc dword[ards_count]
        ;如果ebx不为0，则没有后续的值
        cmp ebx,0
        jnz .next

        mov si,detecting
        call print

        jmp prepare_protected_mode
    
jmp $

print:
    mov ah,0x0e ;通过中断显示字符
    .next:
        mov al, [si]
        cmp al,0
        jz .done
        int 0x10
        inc si
        jmp .next
    .done:
    ret

loading:
    db "Loading MyOs..", 10, 13, 0 ;  10:\n   13:\r
detecting:
    db "Detecting Memory Success..", 10, 13, 0 ;  10:\n   13:\r

error:
    mov si,.msg
    call print
    hlt             ;cpu 进入省电模式
    jmp $
    .msg db "Loading Error!!!", 10, 13, 0


prepare_protected_mode:

    cli;关中断

    ;打开A20
    in al,0x92
    or al,0b10
    out 0x92,al

    ;加载 gdt
    lgdt[gdt_ptr]

    ;启动保护模式
    mov eax,cr0
    or eax,1
    mov cr0,eax

    ;用跳转来刷新缓存，启用保护模式
    jmp dword code_selector:protect_mode

[bits 32]
protect_mode:
    mov ax,data_selector
    mov ds,ax
    mov es,ax
    mov fs,ax
    mov gs,ax
    mov ss,ax           ;初始化段寄存器

    mov esp,0x10000     ;修改栈顶

    mov edi,0x10000  ;读取目标内存
    mov ecx,10       ;起始扇区
    mov bl, 200      ;扇区数量

    call read_disk      ;读取内核

    mov eax, 0x20220511 ; 内核魔数
    mov ebx, ards_count; ards 数量指针

    jmp dword code_selector:0x10000 ;跳转至内核

    ud2

jmp $;阻塞

read_disk:
    ;设置读写扇区的数量
    mov dx,0x1f2
    mov al,bl
    out dx,al

    inc dx      ;0x1f3
    mov al,cl   ;起始扇区的0 ~ 7位
    out dx,al
    
    inc dx      ;0x1f4
    shr ecx,8
    mov al,cl   ;起始扇区的8 ~ 15位
    out dx,al
    
    inc dx      ;0x1f5
    shr ecx,8
    mov al,cl   ;起始扇区的16 ~ 23位
    out dx,al
    
    inc dx      ;0x1f6
    shr ecx,8
    and cl,0b1111;起始扇区的24 ~ 27位

    mov al,0b1110_0000 ;主盘 LBA模式
    or al,cl
    out dx,al
    
    inc dx          ;0x1f7
    mov al,0x20     ;读硬盘
    out dx,al
    
    xor ecx,ecx
    mov cl,bl      ;得到读写扇区的数量

    .read:
        push cx
        call .waits;等待数据准备完毕
        call .reads;读取一个扇区
        pop cx
        loop .read
    ret

    .waits:
        mov dx,0x1f7
        .check:
            in al,dx
            jmp $+2;直接跳转下一行 类似于 nop 但相对于 nop 消耗时钟周期更多
            jmp $+2
            jmp $+2
            and al,0b1000_1000
            cmp al,0b0000_1000
            jnz .check
        ret
    
    .reads:
        mov dx,0x1f0
        mov cx,256
        .readw:
            in ax,dx
            jmp $+2
            jmp $+2
            jmp $+2
            mov [edi],ax
            add edi,2
            loop .readw
        ret

code_selector equ (1 << 3)
data_selector equ (2 << 3)

memory_base equ 0 ;内存开始的位置：基地址

;内存界限 4G / 4K - 1
memory_limit equ ((1024 * 1024 * 1024 * 4) / (1024 * 4)) - 1

gdt_ptr:
    dw (gdt_end - gdt_base) - 1
    dd gdt_base
gdt_base:
    dd 0, 0                             ; NULL 描述符
gdt_code:
    dw memory_limit & 0xffff            ;段界限 0 ~ 15 位
    dw memory_base & 0xffff             ;基地址 0 ~ 15 位
    db (memory_base >> 16) & 0xff       ;基地址 16 ~ 23 位
    ;存在 - dlp 0 - S_代码 - 非依从 - 可读 - 没有被访问过
    db 0b_1_00_1_1_0_1_0                
    ;4K - 32位 - 不是 64 位 - 段界限 16~19
    db 0b1_1_0_0_0000| (memory_limit >> 16) & 0xf
    db (memory_base >> 24) & 0xff       ;基地址 24 ~ 31 位
gdt_data:
    dw memory_limit & 0xffff            ;段界限 0 ~ 15 位
    dw memory_base & 0xffff             ;基地址 0 ~ 15 位
    db (memory_base >> 16) & 0xff       ;基地址 16 ~ 23 位
    ;存在 - dlp 0 - S_数据 - 向上 - 可写 - 没有被访问过
    db 0b_1_00_1_0_0_1_0                
    ;4K - 32位 - 不是 64 位 - 段界限 16~19
    db 0b1_1_0_0_0000| (memory_limit >> 16) & 0xf
    db (memory_base >> 24) & 0xff       ;基地址 24 ~ 31 位

gdt_end:

ards_count:
    dd 0
ards_buffer:
